﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;
using Aspose.Words;
using Aspose.Words.Drawing;
using Aspose.Words.Fields;

namespace MailMergeReplaceAspose.Api
{
    public class MailMergeApi
    {

        public static readonly string PathLicense = AppDomain.CurrentDomain.BaseDirectory + "License";
        public MailMergeApi()
        {
            License licenseWords = new License();
            licenseWords.SetLicense(PathLicense + @"\Aspose[1].Words.lic");
        }
        public static bool ReplaceMailMerge(string source, string destination, string pathSource, string pathDestionation)
        {

            License licenseWords = new License();
            licenseWords.SetLicense(PathLicense + @"\Aspose[1].Words.lic");
            try
            {
                if (String.IsNullOrEmpty(pathSource))
                    return false;
                if (!Directory.Exists(pathDestionation))
                    Directory.CreateDirectory(pathDestionation);
                Document doc = new Document(pathSource);

                string filename = Path.GetFileName(pathSource);
                
                NodeCollection fieldStarts = doc.GetChildNodes(NodeType.FieldStart, true);
                foreach (FieldStart fieldStart in fieldStarts)
                {
                    if (fieldStart.FieldType.Equals(FieldType.FieldMergeField))
                    {
                        MergeField mergeField = new MergeField(fieldStart);
                        if(mergeField.Name == source)
                            mergeField.Name = destination;
                    }
                }


                doc.Save(pathDestionation + @"\" + filename, SaveFormat.Doc);
              
            }
            catch
            {
                throw;
            }
            return true;
        }
        public static bool ReplaceMailMerge(string source, string destination, string[] pathSource, string pathDestionation)
        {

            License licenseWords = new License();
            licenseWords.SetLicense(PathLicense + @"\Aspose.Total.Product.Family.lic");
            try
            {
                if (!pathSource.Any())
                    return false;
                if (!Directory.Exists(pathDestionation))
                    Directory.CreateDirectory(pathDestionation);
                Document doc;
                NodeCollection fieldStarts;
                MergeField mergeField;
                foreach (string path in pathSource)
                {
                    if (File.Exists(path))
                    {
                      

                        string filename = Path.GetFileName(path);
                        if (!string.IsNullOrEmpty(filename))
                        {
                            if (filename.Contains("~") || filename.Contains("$"))
                                continue;
                            doc = new Document(path);
                            fieldStarts = doc.GetChildNodes(NodeType.FieldStart, true);
                            foreach (FieldStart fieldStart in fieldStarts)
                            {
                                if (fieldStart.FieldType.Equals(FieldType.FieldMergeField))
                                {
                                    mergeField = new MergeField(fieldStart);
                                    if (mergeField.Name == source)
                                        mergeField.Name = destination;
                                }
                            }

                            string pathsave = pathDestionation + @"\OutPut";
                            if (!Directory.Exists(pathsave))
                                Directory.CreateDirectory(pathsave);

                            doc.Save(pathsave + @"\" + filename, SaveFormat.Doc);
                        }
                    }
                }
               

            }
            catch
            {
                throw;
            }
            return true;
        }
        public static bool ReplaceMailMergeWithValue(string source,string destination,  string[] pathSource, string pathDestionation)
        {

            try
            {
                if (!pathSource.Any())
                    return false;
                if (!Directory.Exists(pathDestionation))
                    Directory.CreateDirectory(pathDestionation);
                Document doc;
                NodeCollection fieldStarts;
                MergeField mergeField;
                foreach (string path in pathSource)
                {
                    if (File.Exists(path))
                    {


                        string filename = Path.GetFileName(path);
                        doc = new Document(path);
                        doc.Range.Replace(source, destination, false, true);
                        doc.Save(pathDestionation + @"\" + filename, SaveFormat.Doc);
                    }
                }


            }
            catch
            {
                throw;
            }
            return true;
        }
        public static bool ReplaceMailMergeWaterMark(string pathImage, string[] pathSource, string pathDestionation,int height = 10)
        {
            if (pathSource.Length == 0)
                return false;
            if (!Directory.Exists(pathDestionation))
                Directory.CreateDirectory(pathDestionation);
            if(File.Exists(pathImage))
            {
                License licenseWords = new License();
                licenseWords.SetLicense(PathLicense + @"\Aspose.Total.Product.Family.lic");
                Document doc;
                DocumentBuilder builder;
                Shape watermark;
                string filename;
                foreach (string path in pathSource)
                {
                    doc = new Document(path);
                    filename = Path.GetFileName(path);
                    builder = new DocumentBuilder(doc);
                    watermark = new Shape(doc, ShapeType.Image);
                    watermark.ImageData.SetImage(pathImage);
                    //// setting image width and height
                   // watermark.Width = 500;
                   // watermark.Height = 100;
                  //  watermark.Width = doc.a
                    // Image will be directed from the bottom-left to the top-right corner.
                    //watermark.Rotation = -40;
                    // Image will be placed center of page
                    //watermark.RelativeHorizontalPosition = RelativeHorizontalPosition.Page;
                    //watermark.RelativeVerticalPosition = RelativeVerticalPosition.Page;
                    watermark.WrapType = WrapType.None;
                    //watermark.VerticalAlignment = VerticalAlignment.Top;
                    //watermark.HorizontalAlignment = HorizontalAlignment.Left;
                    // Calculate image left and top position so it appears in the centre of the page.
                    watermark.Left = 0;
                    watermark.Top = 0;
                   
                    watermark.BehindText = false;
                    builder.InsertNode(watermark);


                    //Paragraph watermarkPara = new Paragraph(doc);
                    //watermarkPara.AppendChild(watermark);

                    //// Insert the watermark into all headers of each document section.
                    //foreach (Section sect in doc.Sections)
                    //{
                    //    HeaderFooter header = sect.HeadersFooters[HeaderFooterType.HeaderEven];

                    //    if (header == null)
                    //    {
                    //        // There is no header of the specified type in the current section, create it.
                    //        header = new HeaderFooter(sect.Document, HeaderFooterType.HeaderEven);
                    //        sect.HeadersFooters.Add(header);
                    //    }

                    //    // Insert a clone of the watermark into the header.
                    //    header.AppendChild(watermarkPara.Clone(true));
                    //}
                    

                    

                    builder.Document.Save(pathDestionation +@"\" + filename,SaveFormat.Doc);
                }
                return true;
            }
            return false;
        }
        /// <summary>
        /// Represents a facade object for a merge field in a Microsoft Word document.
        /// </summary>
        internal class MergeField
        {
            internal MergeField(FieldStart fieldStart)
            {
                if (fieldStart.Equals(null))
                    throw new ArgumentNullException("fieldStart");
                if (!fieldStart.FieldType.Equals(FieldType.FieldMergeField))
                    throw new ArgumentException("Field start type must be FieldMergeField.");

                mFieldStart = fieldStart;

                // Find the field separator node.
                mFieldSeparator = FindNextSibling(mFieldStart, NodeType.FieldSeparator);
                if (mFieldSeparator == null)
                    throw new InvalidOperationException("Cannot find field separator.");

                // Find the field end node. Normally field end will always be found, but in the example document 
                // there happens to be a paragraph break included in the hyperlink and this puts the field end 
                // in the next paragraph. It will be much more complicated to handle fields which span several 
                // paragraphs correctly, but in this case allowing field end to be null is enough for our purposes.
                mFieldEnd = FindNextSibling(mFieldSeparator, NodeType.FieldEnd);
            }

            /// <summary>
            /// Gets or sets the name of the merge field.
            /// </summary>
            internal string Name
            {
                get
                {
                    return GetTextSameParent(mFieldSeparator.NextSibling, mFieldEnd).Trim('«', '»');
                }
                set
                {
                    // Merge field name is stored in the field result which is a Run 
                    // node between field separator and field end.
                    Run fieldResult = (Run)mFieldSeparator.NextSibling;
                    fieldResult.Text = string.Format("«{0}»", value);

                    // But sometimes the field result can consist of more than one run, delete these runs.
                    RemoveSameParent(fieldResult.NextSibling, mFieldEnd);

                    UpdateFieldCode(value);
                }
            }

            private void UpdateFieldCode(string fieldName)
            {
                // Field code is stored in a Run node between field start and field separator.
                Run fieldCode = (Run)mFieldStart.NextSibling;
                Match match = gRegex.Match(fieldCode.Text);

                string newFieldCode = string.Format(" {0}{1} ", match.Groups["start"].Value, fieldName);
                fieldCode.Text = newFieldCode;

                // But sometimes the field code can consist of more than one run, delete these runs.
                RemoveSameParent(fieldCode.NextSibling, mFieldSeparator);
            }

            /// <summary>
            /// Goes through siblings starting from the start node until it finds a node of the specified type or null.
            /// </summary>
            private static Node FindNextSibling(Node startNode, NodeType nodeType)
            {
                for (Node node = startNode; node != null; node = node.NextSibling)
                {
                    if (node.NodeType.Equals(nodeType))
                        return node;
                }
                return null;
            }

            /// <summary>
            /// Retrieves text from start up to but not including the end node.
            /// </summary>
            private static string GetTextSameParent(Node startNode, Node endNode)
            {
                if ((endNode != null) && (startNode.ParentNode != endNode.ParentNode))
                    throw new ArgumentException("Start and end nodes are expected to have the same parent.");

                StringBuilder builder = new StringBuilder();
                for (Node child = startNode; !child.Equals(endNode); child = child.NextSibling)
                    builder.Append(child.GetText());

                return builder.ToString();
            }

            /// <summary>
            /// Removes nodes from start up to but not including the end node.
            /// Start and end are assumed to have the same parent.
            /// </summary>
            private static void RemoveSameParent(Node startNode, Node endNode)
            {
                if ((endNode != null) && (startNode.ParentNode != endNode.ParentNode))
                    throw new ArgumentException("Start and end nodes are expected to have the same parent.");

                Node curChild = startNode;
                while ((curChild != null) && (curChild != endNode))
                {
                    Node nextChild = curChild.NextSibling;
                    curChild.Remove();
                    curChild = nextChild;
                }
            }

            private readonly Node mFieldStart;
            private readonly Node mFieldSeparator;
            private readonly Node mFieldEnd;

            private static readonly Regex gRegex = new Regex(@"\s*(?<start>MERGEFIELD\s|)(\s|)(?<name>\S+)\s+");
        }
    }
}